#pragma once
#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"
using namespace std;

static const int gcap = 5;
using namespace SemModule;
using namespace MutexModule;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gcap)
        : _rq(cap),
          _cap(cap),
          _blank_sem(cap),
          _p_step(0),
          _data_sem(0),
          _c_step(0)
    {
    }
    void Equeue(const T &in)
    {
        // 生产者
        //_pmutex.Lock();
        // 1.申请信号量，空位置信号量
        _blank_sem.P();
        // 先申请完信号量再申请锁这样效率更高
        // 因为多生产者线程可以先把信号量资源占好然后再竞争锁
        // 而如果是在前面加上锁，其他线程还得等这个线程释放锁了才能申请信号量
        //_pmutex.Lock(); //可以直接使用守护锁
        {
            LockGuard Lockguard(_pmutex);

            // 2.生产
            _rq[_p_step] = in;
            // 3.更新下标
            _p_step++;
            // 4.维护环形特征
            _p_step %= _cap;
        }
        //_pmutex.Unlock();

        // 5.让数据加加
        _data_sem.V();
        //_pmutex.Unlock();
    }

    void Pop(T *out)
    {
        // 消费者
        //_cmutex.Lock();
        // 1.申请信号量，数据信号量
        _data_sem.P();
        //_cmutex.Lock();
        {
            LockGuard lockguard(_cmutex);
            // 2.消费
            *out = _rq[_c_step];
            // 3.更新下标
            _c_step++;
            // 4.维护环形特征
            _c_step %= _cap;
        }
        //_cmutex.Unlock();

        // 5.让空位置加加
        _blank_sem.V();
    }

    ~RingQueue() {}

private:
    vector<T> _rq;
    int _cap;

    Sem _blank_sem; // 生产者需要关注空位置资源
    int _p_step;    // 生产者对应的下标

    Sem _data_sem; // 消费者需要关注数据资源
    int _c_step;   // 消费者对应的下标

    // 维护多生产者间、多消费者间的互斥关系需要两把锁
    Mutex _pmutex;
    Mutex _cmutex;
};